{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Appendix 10.2.1: Your First Simple Tool\n",
    "\n",
    "In the previous lesson we walked through the tool use workflow.  It's time to actually get to work implementing a simple example of tool use.  As a recap, there are up to 4 steps in the tool use process: \n",
    "\n",
    "1. **Provide Claude with tools and a user prompt:** (API request)\n",
    "    * Define the set of tools you want Claude to have access to, including their names, descriptions, and input schemas.\n",
    "    * Provide a user prompt that may require the use of one or more of these tools to answer.\n",
    "\n",
    "2. **Claude uses a tool:** (API response)\n",
    "    * Claude assesses the user prompt and decides whether any of the available tools would help with the user's query or task. If so, it also decides which tool(s) to use and with what input(s).\n",
    "    * Claude outputs a properly formatted tool use request.\n",
    "    * The API response will have a `stop_reason` of `tool_use`, indicating that Claude wants to use an external tool.\n",
    "\n",
    "3. **Extract tool input(s), run code, and return results:** (API request)\n",
    "    * On the client side, you should extract the tool name and input from Claude's tool use request.\n",
    "    * Run the actual tool code on the client side.\n",
    "    * Return the results to Claude by continuing the conversation with a new user message containing a `tool_result` content block.\n",
    "\n",
    "4. **Claude uses the tool result to formulate a response:** (API response)\n",
    "    * After receiving the tool results, Claude will use that information to formulate its final response to the original user prompt."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We're going to start with a simple demonstration that only requires \"talking\" to Claude once (don't worry, we'll get to more exciting examples soon enough!). This means that we won't bother with step 4 yet.  We'll ask Claude to answer a question, Claude will request to use a tool to answer it, and then we'll extract the tool input, run code, and return the resulting value.  \n",
    "\n",
    "Today's large language models struggle with mathematical operations, as evidenced by the following code. \n",
    "\n",
    "We ask Claude to \"Multiply 1984135 by 9343116\": "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%pip install -qU pip\n",
    "%pip install -qUr requirements.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import boto3\n",
    "import json\n",
    "from botocore.exceptions import ClientError\n",
    "session = boto3.Session() # create a boto3 session to dynamically get and set the region name\n",
    "region = session.region_name\n",
    "\n",
    "# Import the hints module from the utils package\n",
    "from utils import hints\n",
    "\n",
    "modelId = 'anthropic.claude-3-sonnet-20240229-v1:0'\n",
    "#modelId = 'anthropic.claude-3-haiku-20240307-v1:0'\n",
    "\n",
    "print(f'Using modelId: {modelId}')\n",
    "print(f'Using region: ', {region})\n",
    "\n",
    "bedrock_client = boto3.client(service_name = 'bedrock-runtime', region_name = region,)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "converse_api_params = {\n",
    "    \"modelId\": modelId,  # Specify the model ID to use\n",
    "    \"messages\": [{\"role\": \"user\", \"content\": [{\"text\": \"Multiply 1984135 by 9343116. Only respond with the result\"}]}],\n",
    "    \"inferenceConfig\": {\"temperature\": 0.0, \"maxTokens\": 400}\n",
    "}\n",
    "\n",
    "response = bedrock_client.converse(**converse_api_params)\n",
    "\n",
    "print(response['output']['message']['content'][0]['text'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll likely get a different answer by running the above code multiple times, but this is one answer Claude responded with: \n",
    "\n",
    "```\n",
    "18593367726060\n",
    "```\n",
    "\n",
    "The actual correct answer is :\n",
    "\n",
    "```\n",
    "18538003464660\n",
    "```\n",
    "Claude was *slightly* off by `55364261400`! "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tool use to the rescue!\n",
    "\n",
    "Claude isn't great at doing complex math, so let's enhance Claude's capabilities by providing access to a calculator tool.  \n",
    "\n",
    "Here's a simple diagram explaining the process: \n",
    "\n",
    "![chickens_calculator.png](./images/chickens_calculator.png)\n",
    "The first step is to define the actual calculator function and make sure it works, indepent of Claude.  We'll write a VERY simple function that expects three arguments:\n",
    "* An operation like \"add\" or \"multiply\"\n",
    "* Two operands\n",
    "\n",
    "Here's a basic implementation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def calculator(operation, operand1, operand2):\n",
    "    if operation == \"add\":\n",
    "        return operand1 + operand2\n",
    "    elif operation == \"subtract\":\n",
    "        return operand1 - operand2\n",
    "    elif operation == \"multiply\":\n",
    "        return operand1 * operand2\n",
    "    elif operation == \"divide\":\n",
    "        if operand2 == 0:\n",
    "            raise ValueError(\"Cannot divide by zero.\")\n",
    "        return operand1 / operand2\n",
    "    else:\n",
    "        raise ValueError(f\"Unsupported operation: {operation}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Please note that this simple function is quite limited in its utility because it can only handle simple expressions like `234 + 213` or `3 * 9`.  The point here is to go through the process of working with tools via a very simple educational example.\n",
    "\n",
    "Let's test out our function and make sure it works."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "calculator(\"add\", 10, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "calculator(\"divide\", 200, 25)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to define our tool and tell Claude about it.  When defining a tool, we follow a very specific format. Each tool definition includes:\n",
    "\n",
    "* `name`: The name of the tool. Must match the regular expression ^[a-zA-Z0-9_-]{1,64}$.\n",
    "* `description`: A detailed plaintext description of what the tool does, when it should be used, and how it behaves.\n",
    "* `input_schema`: A JSON Schema object defining the expected parameters for the tool.\n",
    "\n",
    "Unfamiliar with JSON Schema? [Learn more here](https://json-schema.org/learn/getting-started-step-by-step).\n",
    "\n",
    "Here's a simple example for a hypothetical tool:\n",
    "\n",
    "```json\n",
    "{\n",
    "  \"tools\": [\n",
    "    {\n",
    "      \"toolSpec\": {\n",
    "        \"name\": \"send_email\",\n",
    "        \"description\": \"Sends an email to the specified recipient with the given subject and body.\",\n",
    "        \"inputSchema\": {\n",
    "          \"json\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "              \"to\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The email address of the recipient\"},\n",
    "              \"subject\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The subject line of the email\"},\n",
    "              \"body\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The content of the email message\"}\n",
    "            },\n",
    "            \"required\": [\"to\", \"subject\", \"body\"]\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  ]\n",
    "}\n",
    "```\n",
    "\n",
    "This tool, named `send_email`, expects the following inputs:\n",
    "* `to` which is a string and is required\n",
    "* `subject` which is a string and is required\n",
    "* `body` which is a string and is required\n",
    "\n",
    "\n",
    "Here's another tool definition for a tool called `search_product`: \n",
    "\n",
    "```json\n",
    "{\n",
    "  \"tools\": [\n",
    "    {\n",
    "      \"toolSpec\": {\n",
    "        \"name\": \"search_product\",\n",
    "        \"description\": \"Search for a product by name or keyword and return its current price and availability.\",\n",
    "        \"inputSchema\": {\n",
    "          \"json\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "              \"query\": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \"The product name or search keyword, e.g. 'iPhone 13 Pro' or 'wireless headphones'\"},\n",
    "              \"category\": {\n",
    "                \"type\": \"string\",\n",
    "                \"enum\": [\"electronics\", \"clothing\", \"home\", \"toys\", \"sports\"],\n",
    "                \"description\": \"The product category to narrow down the search results\"},\n",
    "              \"max_price\": {\n",
    "                \"type\": \"number\",\n",
    "                \"description\": \"The maximum price of the product, used to filter the search results\"}\n",
    "            },\n",
    "            \"required\": [\"query\"]\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  ]\n",
    "}\n",
    "```\n",
    "This tool has 3 inputs: \n",
    "* A required `query` string representing the product name or search keyword\n",
    "* An optional `category` string that must be one of the predefined values to narrow down the search.  Notice the `\"enum\"` in the definition.\n",
    "* An optional `max_price` number to filter results below a certain price point"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Our calculator tool definition\n",
    "Let's define the corresponding tool for our calculator function we wrote earlier.  We know that the calculator function has 3 required arguments: \n",
    "* `operation` - which can only be \"add\", \"subtract\", \"multiply\", or \"divide\"\n",
    "* `operand1` which should be a number\n",
    "* `operand2` which should also be a number\n",
    "\n",
    "Here's the tool definition:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "toolConfig = {\n",
    "  \"tools\": [\n",
    "    {\n",
    "      \"toolSpec\": {\n",
    "        \"name\": \"calculator\",\n",
    "        \"description\": \"A simple calculator that performs basic arithmetic operations.\",\n",
    "        \"inputSchema\": {\n",
    "          \"json\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "              \"operation\": {\n",
    "                \"type\": \"string\",\n",
    "                \"enum\": [\"add\", \"subtract\", \"multiply\", \"divide\"],\n",
    "                \"description\": \"The arithmetic operation to perform.\"\n",
    "              },\n",
    "              \"operand1\": {\n",
    "                \"type\": \"number\",\n",
    "                \"description\": \"The first operand.\"},\n",
    "              \"operand2\": {\n",
    "                \"type\": \"number\",\n",
    "                \"description\": \"The second operand.\"}\n",
    "            },\n",
    "            \"required\": [\"operation\", \"operand1\", \"operand2\"]\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  ]\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise\n",
    "\n",
    "Let’s practice writing a properly formatted tool definition using the following function as an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def inventory_lookup(product_name, max_results):\n",
    "    return \"this function doesn't do anything\"\n",
    "    #You do not need to touch this or do anything with it!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This hypothetical `inventory_lookup` function should be called like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inventory_lookup(\"AA batteries\", 4)\n",
    "\n",
    "inventory_lookup(\"birthday candle\", 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Your task is to write a corresponding, properly-formatted tool definition.  Assume both arguments are required in your tool definition."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "### Starter tool definition template ###\n",
    "\n",
    "```json\n",
    "toolConfig = {\n",
    "  \"tools\": [\n",
    "    {\n",
    "      \"toolSpec\": {\n",
    "        \"name\": \"inventory_lookup\",\n",
    "        \"description\": \" \",\n",
    "        \"inputSchema\": {\n",
    "          \"json\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "              \" \": {\n",
    "                \"type\": \"string\",\n",
    "                \"description\": \" \"\n",
    "              },\n",
    "              \" \": {\n",
    "                \"type\": \"number\",\n",
    "                \"description\": \" \"\n",
    "              },\n",
    "            },\n",
    "            \"required\": [\" \", \" \"]\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  ]\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Providing Claude with our tool\n",
    "Now back to our calculator function from earlier. At this point, Claude knows nothing about the calculator tool! It's just a little Python dictionary.  When making our request to Claude, we can pass a list of tools to \"tell\" Claude about.  Let's try it now:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "converse_api_params = {\n",
    "    \"modelId\": modelId,\n",
    "    \"messages\": [{\"role\": \"user\", \"content\": [{\"text\": \"Multiply 1984135 by 9343116. Only respond with the result.\"}]}],\n",
    "    \"toolConfig\": toolConfig, # provide Claude with details about our calculator tool\n",
    "}\n",
    "\n",
    "response = bedrock_client.converse(**converse_api_params)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, let's take a look at the response Claude gives us back:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "response['output']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "{'message': {'role': 'assistant',\n",
    "  'content': [{'toolUse': {'toolUseId': 'tooluse_YOMRWNbNQuCP-BR16tY6mw',\n",
    "     'name': 'calculator', <---------------------------------------------- Claude wants to use the calculator tool\n",
    "     'input': {'operand1': 1984135,\n",
    "      'operand2': 9343116,\n",
    "      'operation': 'multiply'}}}]}}\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You might notice that our response looks a bit different that it normally does! Specifically, instead of a plain `Message` we're now getting a `ToolsMessage`.\n",
    "\n",
    "Additionally, we can check `response['stopReason']` and see that Claude stopped because it decided it was time to use a tool:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "response['stopReason']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`response['output']['message']['content']` contains a list containing a `ToolUseBlock` which itself contains information on the name of the tool and inputs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "response['output']['message']['content'][-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "tool_use = response['output']['message']['content'][-1]\n",
    "tool_name = tool_use['toolUse']['name']\n",
    "tool_inputs = tool_use['toolUse']['input']\n",
    "\n",
    "print(\"The Tool Name Claude Wants To Call:\", tool_name)\n",
    "print(\"The Inputs Claude Wants To Call It With:\", tool_inputs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next step is to simply take the tool name and inputs that Claude provided us with and use them to actually call the calculator function we wrote earlier.  Then we'll have our final answer! "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "operation = tool_inputs[\"operation\"]\n",
    "operand1 = tool_inputs[\"operand1\"]\n",
    "operand2 = tool_inputs[\"operand2\"]\n",
    "\n",
    "result = calculator(operation, operand1, operand2)\n",
    "print(\"RESULT IS\", result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We got the correct answer of `18538003464660`!!! Instead of relying on Claude to get the math correct, we simply ask Claude a question and give it access to a tool it can decide to use if necessary.  \n",
    "\n",
    "#### Important note\n",
    "If we ask Claude something that does not require tool use, in this case something that has nothing to do with math or calculations, we probably want it to respond as normal. Claude will usually do this, but sometimes Claude is very eager to use its tools! \n",
    "\n",
    "Here's an example where sometimes Claude tries to use the calculator even though it doesn't make sense to use it. Let's see what happens when we ask Claude, \"What color are emeralds?\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "converse_api_params = {\n",
    "    \"modelId\": modelId,\n",
    "    \"messages\": [{\"role\": \"user\", \"content\": [{\"text\":\"What color are emeralds?\"}]}],\n",
    "    \"inferenceConfig\": {\"temperature\": 0.0, \"maxTokens\": 400},\n",
    "    \"toolConfig\": toolConfig,\n",
    "}\n",
    "\n",
    "response = bedrock_client.converse(**converse_api_params)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "response['output']['message']['content']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Claude gives us this response: \n",
    "\n",
    "```\n",
    "[{'toolUse': {'toolUseId': 'tooluse_PM0i2kehQnOq9gcRFa8QEg',\n",
    "   'name': 'calculator',\n",
    "   'input': {'operand1': 0, 'operand2': 0, 'operation': 'add'}}}]\n",
    "\n",
    "```\n",
    "Claude wants us to call the calculator tool? A very easy fix is to adjust our prompt or add a system prompt that says something along the lines of: `You have access to tools, but only use them when necessary. If a tool is not required, respond as normal`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "converse_api_params = {\n",
    "    \"modelId\": modelId,\n",
    "    \"system\": [{\"text\": \"You have access to tools, but only use them when necessary. If a tool is not required, respond as normal\"}],\n",
    "    \"messages\": [{\"role\": \"user\", \"content\": [{\"text\":\"What color are emeralds?\"}]}],\n",
    "    \"inferenceConfig\": {\"temperature\": 0.0, \"maxTokens\": 400},\n",
    "    \"toolConfig\": toolConfig,\n",
    "}\n",
    "\n",
    "response = bedrock_client.converse(**converse_api_params)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "response['output']['message']['content'][0]['text']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now Claude responds back with appropriate content and doesn't try to shoehorn tool usage when it doesn't make sense.  This is the new response we get: \n",
    "\n",
    "```\n",
    "'Emeralds are green in color.'\n",
    "```\n",
    "\n",
    "We can also see that `stopReason` is now `end_turn` instead of `tool_use`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "response['stopReason']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Putting it all together"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import re\n",
    "import boto3\n",
    "import json\n",
    "from botocore.exceptions import ClientError\n",
    "\n",
    "\n",
    "def calculator(operation, operand1, operand2):\n",
    "    if operation == \"add\":\n",
    "        return operand1 + operand2\n",
    "    elif operation == \"subtract\":\n",
    "        return operand1 - operand2\n",
    "    elif operation == \"multiply\":\n",
    "        return operand1 * operand2\n",
    "    elif operation == \"divide\":\n",
    "        if operand2 == 0:\n",
    "            raise ValueError(\"Cannot divide by zero.\")\n",
    "        return operand1 / operand2\n",
    "    else:\n",
    "        raise ValueError(f\"Unsupported operation: {operation}\")\n",
    "\n",
    "\n",
    "toolConfig = {\n",
    "  \"tools\": [\n",
    "    {\n",
    "      \"toolSpec\": {\n",
    "        \"name\": \"calculator\",\n",
    "        \"description\": \"A simple calculator that performs basic arithmetic operations.\",\n",
    "        \"inputSchema\": {\n",
    "          \"json\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "              \"operation\": {\n",
    "                \"type\": \"string\",\n",
    "                \"enum\": [\n",
    "                  \"add\", \"subtract\", \"multiply\", \"divide\"],\n",
    "                \"description\": \"The arithmetic operation to perform.\"\n",
    "              },\n",
    "              \"operand1\": {\n",
    "                \"type\": \"number\",\n",
    "                \"description\": \"The first operand.\"\n",
    "              },\n",
    "              \"operand2\": {\n",
    "                \"type\": \"number\",\n",
    "                \"description\": \"The second operand.\"\n",
    "              }\n",
    "            },\n",
    "            \"required\": [\n",
    "              \"operation\", \"operand1\", \"operand2\"]\n",
    "          }\n",
    "        }\n",
    "      }\n",
    "    }\n",
    "  ]\n",
    "}\n",
    "\n",
    "\n",
    "bedrock_client = boto3.client(service_name='bedrock-runtime', region_name=region)\n",
    "\n",
    "\n",
    "def prompt_claude(prompt):\n",
    "\n",
    "    messages = [{\"role\": \"user\", \"content\": [{\"text\": prompt}]}]\n",
    "\n",
    "    converse_api_params = {\n",
    "        \"modelId\": modelId,\n",
    "        \"system\": [{\"text\": \"You have access to tools, but only use them when necessary. If a tool is not required, respond as normal\"}],\n",
    "        \"messages\": messages,\n",
    "        \"inferenceConfig\": {\"temperature\": 0.0, \"maxTokens\": 400},\n",
    "        \"toolConfig\": toolConfig,\n",
    "    }\n",
    "\n",
    "    response = bedrock_client.converse(**converse_api_params)\n",
    "\n",
    "    if response['stopReason'] == \"tool_use\":\n",
    "        tool_use = response['output']['message']['content'][-1]\n",
    "        tool_name = tool_use['toolUse']['name']\n",
    "        tool_inputs = tool_use['toolUse']['input']\n",
    "\n",
    "        if tool_name == \"calculator\":\n",
    "            print(\"Claude wants to use the calculator tool\")\n",
    "            operation = tool_inputs[\"operation\"]\n",
    "            operand1 = tool_inputs[\"operand1\"]\n",
    "            operand2 = tool_inputs[\"operand2\"]\n",
    "\n",
    "            try:\n",
    "                result = calculator(operation, operand1, operand2)\n",
    "                print(\"Calculation result is:\", result)\n",
    "            except ValueError as e:\n",
    "                print(f\"Error: {str(e)}\")\n",
    "\n",
    "    elif response['stopReason'] == \"end_turn\":\n",
    "        print(\"Claude didn't want to use a tool\")\n",
    "        print(\"Claude responded with:\")\n",
    "        print(response['output']['message']['content'][0]['text'])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "prompt_claude(\"I had 23 chickens but 2 flew away.  How many are left?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "prompt_claude(\"What is 201 times 2\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "prompt_claude(\"Write me a haiku about the ocean\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*** "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercise\n",
    "\n",
    "Your task is to help build out a research assistant using Claude.  A user can enter a topic that they want to research and get a list of Wikipedia article links saved to a markdown file for later reading. We could try asking Claude directly to generate a list of article URLs, but Claude is unreliable with URLs and may hallucinate article URLs. Also, legitimate articles might have moved to a new URL after Claude's training cutoff date.  Instead, we're going to use a tool that connects to the real Wikipedia API to make this work! \n",
    "\n",
    "We'll provide Claude with access to a tool that accepts a list of possible Wikipedia article titles that Claude has generated but could have hallucinated. We can use this tool to search Wikipedia to find the actual Wikipedia article titles and URLs to ensure that the final list consists of articles that all actually exist. We’ll then save these article URLs to a markdown file for later reading.\n",
    "\n",
    "We've provided you with two functions to help:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import wikipedia\n",
    "def generate_wikipedia_reading_list(research_topic, article_titles):\n",
    "    wikipedia_articles = []\n",
    "    for t in article_titles:\n",
    "        results = wikipedia.search(t)\n",
    "        try:\n",
    "            page = wikipedia.page(results[0])\n",
    "            title = page.title\n",
    "            url = page.url\n",
    "            wikipedia_articles.append({\"title\": title, \"url\": url})\n",
    "        except:\n",
    "            continue\n",
    "    add_to_research_reading_file(wikipedia_articles, research_topic)\n",
    "\n",
    "def add_to_research_reading_file(articles, topic):\n",
    "    with open(\"output/research_reading.md\", \"a\", encoding=\"utf-8\") as file:\n",
    "        file.write(f\"## {topic} \\n\")\n",
    "        for article in articles:\n",
    "            title = article[\"title\"]\n",
    "            url = article[\"url\"]\n",
    "            file.write(f\"* [{title}]({url}) \\n\")\n",
    "        file.write(f\"\\n\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first function, `generate_wikipedia_reading_list` expects to be passed a research topic like \"The history of Hawaii\" or \"Pirates across the world\" and a list of potential Wikipedia article names that we will have Claude generate.  The function uses the `wikipedia` package to search for corresponding REAL wikipedia pages and builds a list of dictionaries that contain an article's title and URL.\n",
    "\n",
    "Then it calls `add_to_research_reading_file`, passing in the list of Wikipedia article data and the overall research topic.  This function simply adds markdown links to each of the Wikipedia articles to a file called `output/research_reading.md`.  The filename is hardcoded for now, and the function assumes it exists. It exists in this repo, but you'll need to create it yourself if working somewhere else.\n",
    "\n",
    "The idea is that we'll have Claude \"call\" `generate_wikipedia_reading_list` with a list of potential article titles that may or may not be real.  Claude might pass the following input list of article titles, some of which are real Wikipedia articles and some of which are not:\n",
    "\n",
    "```py\n",
    "[\"Piracy\", \"Famous Pirate Ships\", \"Golden Age Of Piracy\", \"List of Pirates\", \"Pirates and Parrots\", \"Piracy in the 21st Century\"]\n",
    "```\n",
    "\n",
    "The `generate_wikipedia_reading_list` function goes through each of those article titles and collects the real article titles and corresponding URLs for any Wikipedia articles that actually exist.  It then calls `add_to_research_reading_file` to write that content to a markdown file for later reference."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The end goal\n",
    "\n",
    "Your job is to implement a function called `get_research_help` that accepts a research topic and a desired number of articles. This function should use Claude to actually generate the list of possible Wikipedia articles and call the `generate_wikipedia_reading_list` function from above.  Here are a few example function calls:\n",
    "\n",
    "```py\n",
    "get_research_help(\"Pirates Across The World\", 7)\n",
    "\n",
    "get_research_help(\"History of Hawaii\", 3)\n",
    "\n",
    "get_research_help(\"are animals conscious?\", 3)\n",
    "```\n",
    "\n",
    "After these 3 function calls, this is what our output `research_reading.md` file looks like (check it out for yourself in output/research_reading.md): \n",
    "\n",
    "![research_reading.png](./images/research_reading.png)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To accomplish this, you'll need to do the following: \n",
    "\n",
    "* Write a tool definition for the `generate_wikipedia_reading_list` function\n",
    "* Implement the `get_research_help` function\n",
    "    * Write a prompt to Claude telling it that you need help gathering research on the specific topic and how many article titles you want it to generate\n",
    "    * Tell Claude about the tool it has access to\n",
    "    * Send off your request to Claude\n",
    "    * Check to see if Claude called the tool.  If it did, you'll need to pass the article titles and topic it generated to the `generate_wikipedia_reading_list` function we gave you. That function will gather actual Wikipedia article links and then call `add_to_research_reading_file` to write the links to `output/research_reading.md`\n",
    "    * Open `output/research_reading.md` to see if it worked!\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Starter Code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Here's your starter code!\n",
    "import wikipedia\n",
    "def generate_wikipedia_reading_list(research_topic, article_titles):\n",
    "    wikipedia_articles = []\n",
    "    for t in article_titles:\n",
    "        results = wikipedia.search(t)\n",
    "        try:\n",
    "            page = wikipedia.page(results[0])\n",
    "            title = page.title\n",
    "            url = page.url\n",
    "            wikipedia_articles.append({\"title\": title, \"url\": url})\n",
    "        except:\n",
    "            continue\n",
    "    add_to_research_reading_file(wikipedia_articles, research_topic)\n",
    "\n",
    "def add_to_research_reading_file(articles, topic):\n",
    "    with open(\"output/research_reading.md\", \"a\", encoding=\"utf-8\") as file:\n",
    "        file.write(f\"## {topic} \\n\")\n",
    "        for article in articles:\n",
    "            title = article[\"title\"]\n",
    "            url = article[\"url\"]\n",
    "            file.write(f\"* [{title}]({url}) \\n\")\n",
    "        file.write(f\"\\n\\n\")\n",
    "        \n",
    "def get_research_help(topic, num_articles=3):\n",
    "   #Implement this function! \n",
    "   pass"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "conda_tensorflow2_p310",
   "language": "python",
   "name": "conda_tensorflow2_p310"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
